﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;

using BeachRunner.Entity;
using BeachRunner.Utility;
using BeachRunner.Tileengine;

namespace BeachRunner.BeachRunner
{
    /// <summary>
    /// Die Spielfigur
    /// 
    /// Autor: Mathias Müller, OLD (Mattis Hänel)
    /// </summary>
    public class Player : Entity.Entity
    {
        /// <summary>
        /// zum steuern der Figur
        /// </summary>
        private PlayerController controller;

        /// <summary>
        /// Wie lange hält sich ein Sprungevent bevor es verworfen wird ( in Frames )
        /// </summary>
        private int jumpEventLength = 10;
        /// <summary>
        /// Wie lange hat das Event noch bevor es verworfen wird
        /// </summary>
        private float jumpTimeOut;

        /// <summary>
        /// ist der Spieler momentan gedukt
        /// </summary>
        private bool isCrouching;

        /// <summary>
        /// Wie viele Wasserpartikel sollen generiert werden wenn der Spieler gegen ein Hinderniss läuft
        /// </summary>
        private int dripsPerCollision = 30;
        /// <summary>
        /// Die Startgeschwindigkeitsvectoren der Wasserpartikel
        /// </summary>
        private Vector2[] dripDirections;
        /// <summary>
        /// Fasst alle Wasserpartikel zu einem Set zusammen
        /// </summary>
        private EntitySet drips;

        /// <summary>
        /// Wie viel Leben verliert der Spieler pro Kollision
        /// </summary>
        private int reducedLifePerCollision;
        /// <summary>
        /// Wie viel Leben verliert der Spieler pro Kollision
        /// </summary>
        public int ReducedLifePerCollision
        {
            get { return reducedLifePerCollision; }
            set { reducedLifePerCollision = value; }
        }

        /// <summary>
        /// Wie viel Punkte verliert der Spieler pro Kollision
        /// </summary>
        private int reducedScorePerCollision;
        /// <summary>
        /// Wie viel Punkte verliert der Spieler pro Kollision
        /// </summary>
        public int ReducedScorePerCollision
        {
            get { return reducedScorePerCollision; }
            set { reducedScorePerCollision = value; }
        }

        /// <summary>
        /// ist der Spieler tot
        /// </summary>
        private bool isDead;
        /// <summary>
        /// ist der Spieler tot
        /// </summary>
        public bool IsDead
        {
            get { return isDead; }
        }

        /// <summary>
        /// Wie viele Frames wird der Spieler nach einer Kollision von einer weiteren bewahrt
        /// </summary>
        private int collisionRelief;

        /// <summary>
        /// Die Grafik für den Spieler
        /// </summary>
        private static SpriteSet spriteSet;

        /// <summary>
        /// Sprungsound
        /// </summary>
        private static SoundEffect jumpSound;
        /// <summary>
        /// Kollisionssound
        /// </summary>
        private static SoundEffect hitSound;
        /// <summary>
        /// Gameoversound
        /// </summary>
        private static SoundEffect gameOverSound;


        /// <summary>
        /// Wie lange muss der Touchscreen gedrückt werden um einen langen Sprung auszulösen in ms
        /// </summary>
        private static int maxCharchedJumpTime;
        /// <summary>
        /// Wie lange wurde der Touchscreen schon grdrückt
        /// </summary>
        private static int jumpTimer;
        /// <summary>
        /// wurde die maximale Sprunghöhe erreicht
        /// </summary>
        private static bool maxJumpReached;
        /// <summary>
        /// wurde der touchscreen losgelassen, wärend die Figur springt
        /// </summary>
        private static bool jumpReleased;


        /// <summary>
        /// Läd alle für die Spielfigur notwendigen Resourcen
        /// </summary>
        /// <param name="contentManager">Der zu verwendende ContentManager</param>
        public static void LoadContent(ContentManager contentManager)
        {
            spriteSet = new SpriteSet(contentManager.Load<Texture2D>("gfx\\player"), 100, 128);

            jumpSound = contentManager.Load<SoundEffect>("sfx\\jump");
            hitSound = contentManager.Load<SoundEffect>("sfx\\hit");
            gameOverSound = contentManager.Load<SoundEffect>("sfx\\gameOver");
        }

        /// <summary>
        /// Erstellt die eine Spielfigur
        /// </summary>
        /// <param name="startPos">Startposition</param>
        /// <param name="collisionLayer">Das für die Kollision zu verwendende Layer</param>
        /// <param name="controller">Der für den Spieler zu verwendende Controller</param>
        public Player(Vector2 startPos, Layer collisionLayer, PlayerController controller)
            : base(null, startPos, 56, 110, null, collisionLayer)
        {
            // die Animationsabläufe für die Figur
            short[][] indexes = new short[7][];
            indexes[0] = new short[] { 0, 1, 2, 3, 4, 5 };
            indexes[1] = new short[] { 6, 7, 8, 9, 10, 11 };
            indexes[2] = new short[] { 12, 13, 14, 14, 14, 15 };
            indexes[3] = new short[] { 18, 19, 20, 21, 22, 23 };
            indexes[4] = new short[] { 24, 25, 26, 27, 28, 29 };
            indexes[5] = new short[] { 30, 31, 32, 32, 32, 33 };
            indexes[6] = new short[] { 36, 37, 38, 39, 40, 41 };

            // die Animationszeiten für die Figur
            short[][] times = new short[7][];
            times[0] = new short[] { 70, 70, 70, 70, 70, 70 };
            times[1] = new short[] { 50, 50, 50, 50, 50, 50 };
            times[2] = new short[] { 70, 70, 70, 70, 70, 70 };
            times[3] = new short[] { 70, 70, 70, 70, 70, 70 };
            times[4] = new short[] { 50, 50, 50, 50, 50, 50 };
            times[5] = new short[] { 70, 70, 70, 70, 70, 70 };
            times[6] = new short[] { 70, 70, 70, 70, 70, 70 };

            animationSet = new AnimationSet(spriteSet, indexes, times);
            // die Animation läuft durchgehend
            animationSet.PlayAnimation();

            // der Offset zwischen Spielerposition und Spielergrafik
            drawOffset = new Vector2(-22, -18);

            this.controller = controller;

            // Geschwindikeitsgrenzen bis zu denen der Spieler beschleunigen kann
            maximalVelocity = new Vector2(5, 32);

            // Wie viel Leben verliert der Spieler bei einer Kollision?
            reducedLifePerCollision = 25;

            // Wie viel Punkte verliert der Spieler bei einer Kollision?
            reducedScorePerCollision = 0;

            // Der Spieler Prallt nirgens ab
            bounciness = new Vector2(0.0f, 0.0f);
            // die reibung die der Spiler durch die Karte erfährt
            friction = new Vector2(1.0f, 0.0f);

            isCrouching = false;
            isDead = false;

            // Der Sprung muss eine Sekunde lang aufgeladen werden um ein hoher Sprung zu sein
            maxCharchedJumpTime = 1000 / Game1.TIME_PER_TICK;
            jumpTimer = 0;
            maxJumpReached = false;
            jumpReleased = false;

            drips = new EntitySet();
            dripDirections = new Vector2[dripsPerCollision];
            float angle = 0;
            float deltaAngle = (float)(Math.PI * 2) / dripsPerCollision;
            Vector2 random;
            Random generator = new Random();
            for (int i = 0; i < dripsPerCollision; i++)
            {
                random = new Vector2((float)(generator.NextDouble()) * 2, (float)(generator.NextDouble()) * 2);
                dripDirections[i] = new Vector2((float)Math.Cos(angle) * 5, (float)Math.Sin(angle) * 10) + random;
                angle += deltaAngle;
            }

            collisionRelief = 0;
        }

        override public void Update()
        {
            if (jumpTimer > 0)
            {
                jumpTimer--;
                maxJumpReached = false;
            }
            else
                maxJumpReached = true;

            if(jumpTimeOut <= 0)
            {                
                    jumpReleased = false;
                    maxJumpReached = false;
                    Game1.Gravity = Game1.DefaultGravity;
            }

            if (standing && !isCrouching && !isDead )
            {
                if (animationSet.AnimationID != 0)
                {
                    animationSet.AnimationID = 0;
                }
            }

            if (controller.IsMoovingRight && !isDead)
            {
                acceleration.X = 0.5f;
                
            }

            if (controller.IsMoovingLeft && !isDead)
            {
                acceleration.X = -0.5f;
            }

            if (controller.IsJumpingEdge && !isDead)
            {
                jumpTimeOut = jumpEventLength;
            }
            if (standing && jumpTimeOut > 0)
            {
                velocity.Y = -15;
                jumpTimer = maxCharchedJumpTime;

                if (animationSet.AnimationID != 2)
                {
                    animationSet.AnimationID = 2;
                }

                if (Game1.Config.Sound_effects)
                {
                    jumpSound.Play();
                }
            }
            if (jumpTimeOut > 0)
            {
                jumpTimeOut--;

                if (IOTouchControl.IsTouchReleased(IOTouchControl.RightArea) && jumpReleased == false)
                    jumpReleased = true;

                Game1.Gravity = Game1.DefaultGravity;
                
                if (maxJumpReached == false && jumpReleased)
                {
                    Game1.Gravity = Game1.DefaultGravity * 2.0f;
                }
            }

            if (controller.IsCrouching && !isDead)
            {
                if (isCrouching == false)
                {
                    position.Y += 55;
                    height = 55;

                    if (animationSet.AnimationID != 1)
                    {
                        animationSet.AnimationID = 1;
                    }
                }

                isCrouching = true;
            }
            else
            {
                if (isCrouching == true)
                {
                    position.Y -= 55;
                    height = 110;
                }

                isCrouching = false;
            }

            Game1.LevelManager.CurrentLevel.Map.collideAndOperate(new Rectangle((int)position.X, (int)position.Y, width, height), Game1.WaterManager);
            Game1.LevelManager.CurrentLevel.Map.collideAndOperate(new Rectangle((int)position.X, (int)position.Y, width, height), Game1.BonusManager);

            if (Game1.BonusManager.ActivateBonusSpeedUp)
            {
                MaximalVelocity = new Vector2(MaximalVelocity.X * Game1.BonusManager.SpeedBonus, MaximalVelocity.Y);
                Game1.BonusManager.ActivateBonusSpeedUp = false;
            }
            if (Game1.BonusManager.ActivateBonusSpeedDown)
            {
                MaximalVelocity = new Vector2(MaximalVelocity.X * Game1.BonusManager.SpeedMalus, MaximalVelocity.Y);
                Game1.BonusManager.ActivateBonusSpeedDown = false;
            }

            if (collisionRelief > 0)
            {
                collisionRelief--;
            }

            if (traceResult.collisionX && collisionRelief == 0)
            {
                collisionRelief = 90;
                Game1.WaterManager.Water -= ReducedLifePerCollision;
                Game1.ScoreManager.Scores -= ReducedScorePerCollision;

                for (int i = 0; i < dripsPerCollision; i++)
                {
                    drips.Add(new Water(drips, position, dripDirections[i], collisionLayer));
                }

                if (Game1.WaterManager.Water <= 0)
                {
                    KillPlayer();
                }

                if (Game1.Config.Sound_effects)
                {
                    hitSound.Play();
                }
            }

            drips.Update();

            base.Update();
        }

        /// <summary>
        /// Player wird "getötet"
        /// </summary>
        public void KillPlayer()
        {
            isDead = true;
            if (Game1.Config.Sound_effects)
            {
                MediaPlayer.Pause();
                gameOverSound.Play();
            }

            velocity = new Vector2(5, -25);
            acceleration = new Vector2(1, 0);
            animationSet.AnimationID = 6;
            animationSet.StopAnimation();
            animationSet.PlayAnimation();
        }

        override public void Draw(SpriteBatch spriteBatch, Vector2 offset)
        {
            drips.Draw(spriteBatch, offset);
            base.Draw(spriteBatch, offset);
        }

        public override void HandleTraceResult(TraceResult result)
        {
            if (!isDead)
            {
                base.HandleTraceResult(result);
            }
            else
            {
                traceResult.collisionX = false;
                traceResult.collisionY = false;
                position += velocity;
            }
        }
    }
}
